home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / DCLAP 6d / dclap6d / DNet.more / DTCP.cp-two < prev    next >
Text File  |  1996-07-05  |  44KB  |  1,800 lines

  1. // DTCP.cp 
  2. // by D. Gilbert, 1991-92, with help from code of Harry Chesley and others 
  3. // 1994 -- rewritten for multi-platform tcp-sockets api
  4.  
  5.  
  6. #include "DTCP.h"
  7. #include <ncbi.h>
  8.  
  9. #ifndef UseMacTCPApi
  10.  
  11. #include "dnet-tcp.h"
  12.  
  13. #ifdef OS_MAC
  14.     // leave only CR at end
  15. char*    DTCP::LineEnd = "\015";
  16. short DTCP::LineEndSize = 1;
  17. #endif
  18. #ifdef OS_UNIX
  19.     // leave only LF at end
  20. char*    DTCP::LineEnd = "\012";
  21. short DTCP::LineEndSize = 1;
  22. #endif
  23. #ifdef OS_DOS
  24.     // CR + LF
  25. char*    DTCP::LineEnd = "\015\012";
  26. short DTCP::LineEndSize = 2;
  27. #endif
  28.  
  29.  
  30. #else    /* UseMacTCPApi */
  31.  
  32. #include "includes.h"
  33. #ifndef __STDLIB__
  34. #include <StdLib.h>
  35. #endif
  36. #ifndef __STDIO__
  37. #include <StdIO.h>
  38. #endif
  39. #ifndef __STRING__
  40. #include <STRING.h>
  41. #endif
  42.  
  43. #ifdef BOZOTHREADS
  44. # include <UAThread.h>
  45. #else
  46. # include <UThread.h>
  47. #endif
  48.  
  49. //#include "UMacTCP.h"
  50. #include "UTCP.h"
  51. #include "AddressXlation.h"        // Apple MacTCP header
  52. #include "GetMyIPAddr.h"            // Apple MacTCP header
  53. #include <UMacAppUtilities.h>    //dgg
  54.  
  55. #endif        /* UseMacTCPApi */
  56.  
  57.  
  58.  
  59. #ifdef UseMacTCPApi 
  60.  
  61. typedef wdsEntry *wdsEntryPtr;
  62. typedef rdsEntry *rdsEntryPtr;
  63.  
  64. const short        errTCPError    = 0x8234;
  65. const long        msgTCPError = 0x80001234;
  66.  
  67. #endif        // UseMacTCPApi 
  68.  
  69.  
  70. const    short        kUTCPVersion    = 3;
  71.  
  72. #ifndef UseMacTCPApi 
  73. const    char*        kUTCPVersionString = "vers. 3.0, Jan 94 (sockets)";
  74. #else
  75. const    char*        kUTCPVersionString = "vers. 3.0, Jan 94 (mactcp)";
  76. #endif
  77.  
  78. const char        kLF        = 10;    
  79. const char        kCR        = 13;    
  80.  
  81. const short        kRecvChunkSize = 500;       // bytes/ handle increment
  82.  
  83.     // make this enum ?    
  84. const short        kErrConnectionBroken = -1; //common way for gopher server to end transmission 
  85. const short        kErrTimedOut = -2;
  86. const short        kErrUserBreak = -3;
  87. const short        kErrMemFull = -4;
  88. const short        kErrOpenTimeout = -5;
  89.  
  90.  
  91. const    short     kTCPDefaultTimeout = 1200;    // default # ticks (1/60sec) to wait for TCP remote
  92. const long         kDefaultOpenDelay = 20;
  93.  
  94.         // this is a guess -- no generic way to find memory available
  95. const    long    kMaxReadBuffer = 32000;
  96.                         
  97. const long     kStreamBufferSize = 24 * 1024;
  98.                         // maybe making this longer will speed up reads !?, 
  99.                         // 8K is suggested minimum, 16K average, up to 128K may speed up usage
  100.  
  101. const    short    kNumRds = 21;
  102. const    short    kNumWds = 21;
  103.                         // used in reading data from stream buffer...
  104.  
  105.  
  106.  
  107.     // Public Global vars
  108. long        gTCPTimeout = kTCPDefaultTimeout;
  109.  
  110. short     gMacTCPRefNum = 0;        // not a global now...
  111.  
  112.     // Private global vars
  113. long      gMyIP = 0;
  114. char*         gMyDotName = NULL; 
  115. Boolean gResolverIsOpen = false;
  116.  
  117.  
  118.     // Debugging vars
  119. const    char *kGetFile     = "TCP.get";
  120. const    char *kPutFile    = "TCP.put";
  121. FILE *    gPutFile;
  122. Boolean gUseTCP = false, gDebugging = false;
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130. Boolean    localUserBreak()
  131. {  
  132.     Boolean        wantbreak = false;
  133.             // need to redo all of this as a DTaskMaster call...
  134.     return wantbreak;
  135. }
  136.  
  137.  
  138.  
  139.  
  140.  
  141.  
  142.  
  143.  
  144. #ifndef    UseMacTCPApi
  145.  
  146.     
  147.  
  148.  
  149. #endif    // UseMacTCPApi
  150.  
  151. short OpenMacTCP()
  152. {
  153.     if (gMacTCPRefNum !=0 ) 
  154.         return 0;
  155.     else {
  156.         gUseTCP = true;
  157.         short err= 0;
  158. #ifdef UseMacTCPApi
  159.         short refnum;
  160.         err= opendriver( ".IPP", &refnum);
  161.         if (err == 0) gMacTCPRefNum= refnum;
  162.         if (!gResolverIsOpen)    {
  163.             FailOSErr(OpenResolver(NULL)); // hostfile
  164.             gResolverIsOpen = true;
  165.             }
  166. #endif
  167.         return err;
  168.         }
  169. }
  170.  
  171.  
  172. void InitializeTCP()         // InitUMacTCP()
  173. {
  174.     gMyDotName = NULL;
  175.     gMacTCPRefNum= 0;
  176.     short err= OpenMacTCP(); 
  177. }
  178.  
  179. void FinishTCP()                 // CloseDownUMacTCP()
  180. {
  181. #ifdef UseMacTCPApi
  182.     if (gResolverIsOpen) {
  183.         gResolverIsOpen = false;
  184.         if (gUseTCP)     { (void) CloseResolver(); }
  185.       }
  186. #endif
  187.     gMacTCPRefNum = 0;
  188. }
  189.  
  190. Boolean TCPIsItInstalled()         // IsMacTCPInstalled()
  191. {
  192.     return (0 == OpenMacTCP());
  193. }
  194.  
  195. long MyIP()
  196. {
  197.     if (!OpenMacTCP() || gMyIP)  return gMyIP;
  198.  
  199. #ifndef UseMacTCPApi 
  200.     gMyIP = MyIPaddress();
  201. #else
  202.     GetAddrParamBlock pb;
  203.     pb.ioCompletion    = nil;
  204.     pb.ioCRefNum    = gMacTCPRefNum;
  205.     pb.csCode            = ipctlGetAddr;
  206.     pb.ioResult     = 1;
  207.     short err = PBControlSync((ParmBlkPtr) &pb);
  208.     if (0 == err) {
  209.         gMyIP = pb.ourAddress;
  210.         //gNetMask    = pb.ourNetMask;
  211.         }
  212. #endif         
  213.     return gMyIP;
  214. }
  215.  
  216.  
  217. void GetMyDotName(char* myDotName)
  218. {
  219.     if (!OpenMacTCP()) {
  220.         myDotName = gMyDotName = ".";
  221.         return;
  222.         }
  223.     if (myDotName) gMyDotName= IP2DotName(MyIP());
  224.     myDotName = gMyDotName;
  225. }    
  226.  
  227.  
  228. #ifdef UseMacTCPApi
  229. pascal void    StrToAddrResultProc(struct hostInfo , Ptr )    
  230. {
  231.     // utility routine for StrToAddr 
  232.     // simply watch the aHostInfo.rtnCode! 
  233. }
  234. #endif
  235.  
  236. char* IP2DotName(long ip)
  237. {
  238.     char *name = NULL;
  239.     
  240. #ifndef UseMacTCPApi
  241.     
  242.     // ?? do we need this proc
  243.     
  244. #else
  245.     static hostInfo theHostInfo; // may not be on stack
  246.     if (!gResolverIsOpen) {
  247.         FailOSErr(OpenResolver(NULL)); // hostfile
  248.         gResolverIsOpen = true;
  249.         }
  250.  
  251.         // ask the DNR function to get the IP address 
  252.     short err= AddrToName( ip, &theHostInfo, (ResultProcPtr) StrToAddrResultProc, (Ptr) 0);
  253.     
  254.         // wait for the address information or some error other than cacheFault to occur 
  255.     while (cacheFault == theHostInfo.rtnCode)
  256.         if (localUserBreak()) ;  
  257.  
  258.     err = short(theHostInfo.rtnCode);
  259.     if (err != cacheFault) Fail(err);
  260.     if (theHostInfo.addr[0] == 0 || theHostInfo.addr[0] == 0xFFFFFFFF)
  261.         Fail(authNameErr);
  262.     theHostInfo.cname[254] = 0;
  263.     name= StrDup( theHostInfo.cname);
  264.     //if (name[name.Length()] == '.') name.Length()--; 
  265. #endif
  266.     return name;
  267. }
  268.  
  269.  
  270.  
  271.  
  272. long DotName2IP(const char* name)
  273. {
  274. #ifndef UseMacTCPApi
  275.     
  276.     return Hostname2IP( name);
  277.     
  278. #else    //UseMacTCPApi
  279.     static hostInfo theHostInfo; // may not be on the stack, like gResolverDone!
  280.     long a, b, c, d;
  281.     if (sscanf( name, "%ld.%ld.%ld.%ld", &a, &b, &c, &d) == 4)
  282.         return (a << 24) | (b << 16) | (c << 8) | d;
  283.  
  284.     if (!gResolverIsOpen)    {
  285.         FailOSErr(OpenResolver(NULL)); // hostfile
  286.         gResolverIsOpen = true;
  287.         }
  288.         
  289.         // ask the DNR function to get the IP address 
  290.     short err= StrToAddr( (char*)name, &theHostInfo, 
  291.                                         (ResultProcPtr) StrToAddrResultProc, (Ptr) 0);
  292.     
  293.         // wait for the address information or some error other than cacheFault to occur 
  294.     while (cacheFault == theHostInfo.rtnCode)
  295.         if (localUserBreak()) ; 
  296.  
  297.     err = short(theHostInfo.rtnCode);
  298.     // #define    outOfMemory                -23048 << getting this err here at times !!
  299.     if (err != 0 && err != cacheFault) FailOSErr(err);
  300.     fi.Success();
  301.     
  302.     // ?? can we leave resolver open b/n calls??
  303.     // having DNR failures after some calls !? will close/open each call fix??
  304.     err = CloseResolver();
  305.     gResolverIsOpen = false;
  306.  
  307.     if (theHostInfo.addr[0] == 0 || theHostInfo.addr[0] == 0xFFFFFFFF)
  308.         return 0;  
  309.     else 
  310.         return theHostInfo.addr[0];
  311.  
  312. #endif    //UseMacTCPApi
  313.  
  314. }
  315.  
  316.  
  317.  
  318.  
  319. // DTCP ..........................
  320.  
  321. void DTCP::Initialize()
  322. {
  323.     fSocket= -1;
  324.     fError= NULL;
  325.     fErrNo= 0;
  326.     fFailed= false;
  327.     fTimeout= gTCPTimeout;
  328.     fLastSentCRLF= false;
  329.     
  330.     fDoShowProgress= false;
  331.     fResultSize= 0;
  332.     fMaxResultSize= 0;
  333.     fResultNew= 0;
  334.     fBytesread= 0;
  335.     fResultTotal= 0; // this is now a ShowProgress::LastBytesRead counter
  336.     fLimitResultSize= 0;
  337.     fEndofMessage= false;
  338.     fResultHand= NULL;
  339.     fNullTerm= true; //??
  340.     fLastc = fLast2c= fLast3c = 0; // receive -- CRLF conversion record
  341.     fStartTime= 0;
  342.     fConnectTime= 0;
  343.  
  344.     fMessageProc = NULL;
  345.     fMessageObj = NULL;
  346.     fBreakProc = NULL;
  347.     fBreakObj = NULL;
  348.     fReadSaveLen = 0;
  349.     fReadSave = NULL;
  350.  
  351. #ifdef UseMacTCPApi    
  352.         // TMacTCP vars
  353.     fConnectionIsOpen = false;
  354.     fStreamIsOpen = false;
  355.     fPBP = NULL;
  356.     fReceivePBP = NULL;
  357.     fAbortPBP = NULL;
  358.     fStreamP = NULL;  
  359.     fWdsEntryP = NULL;
  360.     fRdsEntryP = NULL;
  361.     fWaitingP = NULL;
  362.     fWaitSeg= 0;
  363.     fWaitIndx= 0;
  364.     fHasRDS = false;
  365.     fStreamBufferP = NULL;
  366.     fGetFile= 0;
  367. #endif
  368.  
  369.     (void) OpenMacTCP();
  370.  
  371. #ifdef UseMacTCPApi
  372.         short i;
  373.         
  374.         fPBP = (TCPiopb *)MemNew(sizeof(TCPiopb));
  375.         BlockSet( (Ptr)fPBP, sizeof(TCPiopb), 0);
  376.         fPBP->ioResult = 0; // signal that previous command has ended
  377.  
  378.         fAbortPBP = (TCPiopb *)MemNew(sizeof(TCPiopb));
  379.         BlockSet((Ptr)fAbortPBP, sizeof(TCPiopb), 0);
  380.  
  381.         fReceivePBP = (TCPiopb *)MemNew(sizeof(TCPiopb));
  382.         BlockSet((Ptr)fReceivePBP, sizeof(TCPiopb), 0);
  383.         fReceivePBP->ioResult = 0; // signal that previous command has ended
  384.         fReceivePBP->csParam.receive.rcvBuffLen = 0; // nothing received
  385.  
  386.         fWdsEntryP = MemNew(kNumWds * sizeof(wdsEntry));
  387.         for (i= 0; i<kNumWds; i++) {
  388.             ((wdsEntryPtr)fWdsEntryP)[i].ptr = NULL;
  389.             ((wdsEntryPtr)fWdsEntryP)[i].length = 0;
  390.             }
  391.     
  392.         fRdsEntryP = MemNew(kNumRds * sizeof(rdsEntry));
  393.         for (i= 0; i<kNumRds; i++) {
  394.             ((rdsEntryPtr)fRdsEntryP)[i].ptr = NULL;
  395.             ((rdsEntryPtr)fRdsEntryP)[i].length = 0;
  396.             }
  397.     
  398.         fWaitingP = MemNew(kNumRds * sizeof(rdsEntry));
  399.         for (i= 0; i<kNumRds; i++) {
  400.             ((rdsEntryPtr)fWaitingP)[i].ptr = NULL;
  401.             ((rdsEntryPtr)fWaitingP)[i].length = 0;
  402.             }
  403.             
  404.         fStreamBufferP = MemNew(kStreamBufferSize);
  405.         fStreamP = CreateStream(fStreamBufferP, kStreamBufferSize); //fPBP, 
  406.  
  407. #endif // UseMacTCPApi
  408. }
  409.  
  410.  
  411. DTCP::DTCP()
  412. {
  413.     Initialize();
  414. }
  415.  
  416.  
  417.  
  418.  
  419. DTCP::~DTCP() 
  420. {
  421. #ifndef UseMacTCPApi
  422.     if (fConnectionIsOpen)  
  423.         Close();
  424.     if (fStreamIsOpen)  
  425.         Release();
  426. #else
  427.     if (fConnectionIsOpen) {
  428.         if (fHasRDS) {
  429.             ReturnRds(fWaitingP);
  430.             ReturnRds(fRdsEntryP);
  431.             fHasRDS = false;
  432.             }
  433.         Close();
  434.         }
  435.     if (fStreamIsOpen) {
  436.         Release();
  437.         }
  438.     fStreamBufferP = MemFree(fStreamBufferP);
  439.     MemFree((Ptr)fAbortPBP); fAbortPBP = NULL;
  440.     MemFree((Ptr)fPBP); fPBP = NULL;
  441.     MemFree((Ptr)fReceivePBP); fReceivePBP = NULL;
  442.     MemFree((Ptr)fWdsEntryP); fWdsEntryP = NULL;
  443.     MemFree((Ptr)fRdsEntryP); fRdsEntryP = NULL;
  444.     MemFree((Ptr)fWaitingP); fWaitingP = NULL;
  445.     fStreamP = NULL; // TCPRelease handles release of this
  446. #endif
  447. }
  448.  
  449.  
  450. #ifdef UseMacTCPApi
  451. #define SetupTCPblock( pb, tcpCode, stream) { \
  452.             pb->csCode        = tcpCode; \
  453.             pb->tcpStream    = stream; \
  454.             pb->ioCRefNum    = gMacTCPRefNum; \
  455.             pb->ioCompletion = NULL; \
  456.             pb->ioResult     = 1; }
  457. #endif
  458.  
  459.  
  460.  
  461. Boolean DTCP::UserBreak()
  462. {  
  463.     if (fBreakProc)
  464.         return (fBreakProc)(fBreakObj);
  465.     else
  466.         return false;
  467. }    
  468.  
  469. void DTCP::StreamYieldTime()
  470. {
  471.     if (UserBreak()) Fail(-1); //??
  472. }
  473.  
  474.  
  475. void DTCP::Fail(long errNo)
  476. {
  477.     if (errNo) {
  478.         fErrNo= errNo;
  479.         fError= NULL;
  480.         fFailed= true;
  481.         Nlm_Message (MSG_ERROR, "Network DTCP error # %d",errNo);
  482.         }
  483. }
  484.  
  485.  
  486. void DTCP::Fail(const char* msg)
  487. {
  488.     if (msg) {
  489.         fErrNo= 0;
  490.         fError= (char*) msg;
  491.         fFailed= true;
  492.         Nlm_Message (MSG_ERROR, (char*) msg);
  493.         }
  494. }
  495.  
  496. Boolean DTCP::Failed()
  497. {
  498.     return fFailed;
  499. }
  500.  
  501.  
  502.  
  503. void DTCP::InstallUserBreak(TCPUserBreakHandler aproc, DTaskMaster* itsObject)
  504. {
  505.     fBreakProc= aproc;
  506.     fBreakObj= itsObject;
  507. }
  508.  
  509. void DTCP::InstallMessageHandler(TCPMessageHandler aproc, DTaskMaster* itsObject)
  510. {
  511.     fMessageProc= aproc;
  512.     fMessageObj= itsObject;
  513. }
  514.  
  515. void DTCP::ShowMessage(const char* msg)
  516. {
  517.     if (fMessageProc) (fMessageProc)(fMessageObj, 0, msg);
  518. }
  519.  
  520. Boolean DTCP::IsTCPInstalled()
  521. {
  522.     return TCPIsItInstalled();
  523. }
  524.  
  525.  
  526. long DTCP::NameToAddress(const char* hostName)
  527. {
  528.     return DotName2IP(hostName);
  529. }
  530.  
  531. char* DTCP::AddressToName(long address)
  532. {
  533.     return IP2DotName(address);
  534. }
  535.  
  536. char* DTCP::DotAddrToName(const char* dotAddress)
  537. {
  538.     return IP2DotName( DotName2IP( dotAddress));
  539. }
  540.  
  541.  
  542.         // do we want both of these??
  543. long DTCP::Version()
  544. {
  545.     return kUTCPVersion;
  546. }
  547.  
  548. const char* DTCP::VersionString()
  549. {
  550.     return kUTCPVersionString;
  551. }
  552.  
  553.  
  554. char* DTCP::StatusString(short state)
  555. {
  556.     switch (state) {
  557.         case kTCPlistening    : return "TCP listening";
  558.         case kTCPwaitingforopen: return "TCP waiting for open";
  559.         case kTCPopening        : return "TCP opening";
  560.          case kTCPestablished: return "TCP established";
  561.         case kTCPPleaseClose: return "TCP please close";
  562.         case kTCPclosing        : return "TCP closing";
  563.         case kTCPclosed            : return "TCP closed";
  564.         case kTCPreleased        : return "TCP released";
  565.         case kTCPUnknownState:
  566.         default                            : return "TCP unknown state";
  567.         }
  568. }
  569.  
  570.  
  571. long DTCP::Status()
  572. {
  573. #ifndef UseMacTCPApi
  574.     if (!fStreamIsOpen)                return kTCPreleased;
  575.     else if (!fConnectionIsOpen)     return kTCPPleaseClose;
  576.     else                                          return kTCPestablished;
  577.  
  578. #else
  579.     if (!fStreamIsOpen) 
  580.         return kTCPreleased;
  581.     else {
  582.         // Check for an open still in progress 
  583.         if (fPBP->ioResult > 0) 
  584.             return kTCPwaitingforopen; // is this proper useof ioResult now?
  585.         else {
  586.             SetupTCPblock( fPBP, TCPStatus, fStreamP);
  587.             fPBP->csParam.status.userDataPtr = NULL;
  588.  
  589.             if (PBControlSync((ParmBlkPtr)fPBP) != 0)
  590.                 return kTCPclosed;
  591.             else switch (fPBP->csParam.status.connectionState) {
  592.                     case 0 : return kTCPclosed;
  593.                     case 2 : return kTCPlistening;
  594.                     case 4 :
  595.                     case 6 : return kTCPopening;
  596.                     case 8 : return kTCPestablished;
  597.                     case 10:
  598.                     case 12:
  599.                     case 16:
  600.                     case 18:
  601.                     case 20: return kTCPclosing;
  602.                     case 14: return kTCPPleaseClose;
  603.                     default: return kTCPUnknownState;
  604.                     }
  605.             }
  606.         }
  607.     
  608. #endif
  609. }
  610.  
  611. void DTCP::StatusMessage(void)
  612. {
  613.     ShowMessage( StatusString(Status()));
  614. }
  615.  
  616. Boolean DTCP::EndOfMessage()
  617. {
  618.     return fEndofMessage;
  619.     //?? return (fEndofMessage || (NewBytesReceived == 0));
  620.     //^^ bad when linefeeds are received but don't count in NewBytesRec...!!!
  621. }
  622.  
  623. void DTCP::SetEndOfMessage( Boolean isEnded)
  624. {
  625.     fEndofMessage= isEnded;
  626. }
  627.  
  628.  
  629. //ncbi.h:     time_t Nlm_GetSecs() == time() call, should be okay on all ansi c systems?
  630.  
  631. Boolean DTCP::WaitedForOpen(long delayticks)
  632. {
  633.     long        aTicks;
  634.     short        currStat, oldStat;
  635.     
  636.     oldStat= -123;
  637.     while (true) {
  638.         // Delay(5, &aTicks); << what is ncbi/generic equivalend ??
  639.         //for (aTicks= GetSecs() + 5; GetSecs() < aTicks; ) ;
  640.         
  641.         currStat= (short)Status();
  642.         if (currStat!=oldStat) ShowMessage( StatusString(currStat));
  643.         switch (currStat) {
  644.         
  645.             case kTCPestablished:  
  646.                 if (delayticks) 
  647.                 //for (aTicks= GetSecs() + delayticks; GetSecs() < aTicks; ) ;
  648.                 // ^^ this is TOO LONG
  649.                 return true;
  650.  
  651.             case kTCPUnknownState:
  652.             case kTCPPleaseClose:
  653.             case kTCPreleased:
  654.             case kTCPclosed    :  
  655.                 Fail("Couldn't connect to TCP service.");
  656.                 Release();
  657.                 return false;
  658.                 
  659.             default: if (UserBreak()) {
  660.                 Release();
  661.                 return false;
  662.                 }
  663.             }
  664.         oldStat= currStat;
  665.         }
  666.     return false;
  667. }
  668.  
  669. Boolean DTCP::WaitedForOpen()
  670. {
  671.     return WaitedForOpen(kDefaultOpenDelay);
  672. }
  673.  
  674.  
  675.  
  676. void DTCP::SetShowProgress( Boolean turnOn)
  677. {
  678.     fDoShowProgress= turnOn;
  679. }
  680.  
  681. void DTCP::ShowProgress( long sendRecvCount)
  682. {
  683.     char msg[128];
  684.     sprintf( msg, "TCP bytes sent: %d", sendRecvCount);
  685.     ShowMessage(msg);
  686. }
  687.  
  688. void DTCP::ShowProgress()
  689. {
  690.     if (fBytesread > fResultTotal + 1024) {
  691.         ShowProgress( fBytesread);
  692.         fResultTotal= fBytesread;
  693.         }
  694. }
  695.  
  696.  
  697.  
  698. #ifdef UseMacTCPApi
  699. StreamPtr DTCP::CreateStream(Ptr bufferP, unsigned long bufSize)
  700. {
  701.     SetupTCPblock( fPBP, TCPCreate, nil);
  702.     fPBP->csParam.create.rcvBuff            = bufferP;    
  703.     fPBP->csParam.create.rcvBuffLen        = bufSize;    
  704.     fPBP->csParam.create.notifyProc        = NULL;
  705.     fPBP->csParam.create.userDataPtr    = NULL;
  706.     
  707.     PBControlSync((ParmBlkPtr) fPBP);
  708.     FailOSErr(fPBP->ioResult);
  709.     fStreamIsOpen = true;
  710.     return fPBP->tcpStream;
  711. }
  712. #endif // UseMacTCPApi
  713.  
  714.  
  715. void DTCP::Release()
  716. {
  717.     fBytesread= 0;
  718.     fStreamIsOpen = false;
  719. #ifndef UseMacTCPApi
  720.     if (fSocket>=0) short err= SockClose(fSocket); 
  721.     fSocket= -1;
  722.     fConnectionIsOpen = false;
  723. #else
  724.     SetupTCPblock( fPBP, TCPRelease, fStreamP);        
  725.     PBControlSync((ParmBlkPtr)fPBP);
  726.     Fail(fPBP->ioResult);
  727. #endif     
  728. }
  729.  
  730.  
  731.  
  732. void DTCP::Open( char* hostname, unsigned short hostport, unsigned short localport)
  733. {
  734.     fBytesread= 0;
  735.     fLastc= fLast2c= fLast3c= 0;
  736.     fConnectTime= 0;
  737.     fStartTime=    GetSecs();
  738.     fLastSentCRLF= false;
  739.     fConnectionIsOpen= false;
  740.     
  741. #ifndef UseMacTCPApi
  742.  
  743.     fSocket= SockOpen( hostname, hostport);
  744.     if (fSocket<0) switch (fSocket) {
  745.         case errHost        :    Fail("Can't resolve host name"); return;
  746.         case errSocket    : Fail("Can't create IP socket"); return;
  747.         case errConnect    : Fail("Can't connect to host");    return;
  748.         default                    : Fail("Can't open IP connection"); return;
  749.         }
  750.     fStreamIsOpen = true;
  751.     fConnectionIsOpen = true;
  752.  
  753. #else
  754.  
  755.     long hostaddr=  NameToAddress(hostname);
  756.  
  757.     SetupTCPblock( fPBP, TCPActiveOpen, fStreamP);
  758.     fPBP->csParam.open.ulpTimeoutValue    = (unsigned char)Min(100, fTimeout / 60);    
  759.     fPBP->csParam.open.ulpTimeoutAction    = 1;    
  760.     fPBP->csParam.open.validityFlags        = 0xC0; // timeout val & action flagged    
  761.     //fPBP->csParam.open.validityFlags    = 0x10 + 0x20 + 0x40 + 0x80;    
  762.                                                               // tosFlags + precedence + timeout val + timeout act
  763.     fPBP->csParam.open.commandTimeoutValue= (unsigned char)Min(100, fTimeout / 60);     
  764.     fPBP->csParam.open.remoteHost            = hostaddr;    
  765.     fPBP->csParam.open.remotePort            = hostport;    
  766.     fPBP->csParam.open.localHost            = 0;    
  767.     fPBP->csParam.open.localPort            = 0; //localPort;    
  768.     fPBP->csParam.open.tosFlags                = 0; //3  service type: 0x01=low delay + 0x02=high thruput    
  769.     fPBP->csParam.open.precedence            = 0; //0= default, 1 priority, 2    immediate, 3    flash
  770.     fPBP->csParam.open.dontFrag                = 0;    
  771.     fPBP->csParam.open.timeToLive            = 0;
  772.     fPBP->csParam.open.security                = 0;    
  773.     fPBP->csParam.open.optionCnt            = 0;
  774.     for (short index = 0; index < sizeof(fPBP->csParam.open.options); ++index)    
  775.         fPBP->csParam.open.options[index]    = 0;    
  776.     fPBP->csParam.open.userDataPtr        = NULL;
  777.     
  778.     long startTick = GetSecs();
  779.     PBControlAsync((ParmBlkPtr) fPBP);
  780.     while (fPBP->ioResult == inProgress) StreamYieldTime();
  781.  
  782.     long ticks = GetSecs() - startTick; // do because MacTCP returns timeOut as openFailed
  783.     if (ticks >= fTimeout - 30) Fail(kErrOpenTimeout);
  784.     else Fail(fPBP->ioResult);
  785.     fConnectionIsOpen = true;
  786.     
  787. #endif
  788. }
  789.  
  790.  
  791.  
  792. // CloseConnection aborts the connection if it could not be closed
  793. void DTCP::Close()
  794. {
  795.     fConnectTime = GetSecs() - fStartTime;
  796.  
  797. #ifndef UseMacTCPApi
  798.     if (fSocket>=0) short err= SockClose(fSocket); 
  799.     fSocket= -1;
  800.     fConnectionIsOpen = false;
  801.  
  802. #else
  803.     fConnectionIsOpen = false;
  804.  
  805.     SetupTCPblock( fAbortPBP, TCPClose, fStreamP);        
  806.     fAbortPBP->csParam.close.ulpTimeoutValue    = 30;    
  807.     fAbortPBP->csParam.close.ulpTimeoutAction    = 1;    // 0 == report, 1 == abort
  808.     fAbortPBP->csParam.close.validityFlags        = 0xC0;    
  809.     fAbortPBP->csParam.close.userDataPtr            = NULL;
  810.     PBControlAsync((ParmBlkPtr) fAbortPBP);
  811.  
  812.     long maxtime = GetSecs() + 60;
  813.     while ((fAbortPBP->ioResult == inProgress || fAbortPBP->ioResult == connectionClosing)
  814.         && GetSecs() < maxtime) {
  815.         // MacTCP bug -- can fail to close quickly even w/ TimeoutValue
  816.         // -- should time out then abort if need be
  817.         if (! UserBreak()) ; 
  818.      }
  819.     if (fAbortPBP->ioResult == 0 || fAbortPBP->ioResult == connectionDoesntExist || fAbortPBP->ioResult == connectionTerminated) 
  820.         ;
  821.     else
  822.         Abort(); // timeout: abort the connection
  823.  
  824. #endif
  825. }
  826.  
  827.  
  828. void DTCP::Abort()
  829. {
  830.  
  831. #ifndef UseMacTCPApi
  832.     if (fSocket>=0) short err= SockClose(fSocket); 
  833.     fSocket= -1;
  834.     fConnectionIsOpen = false;
  835. #else
  836.     SetupTCPblock( fAbortPBP, TCPAbort, fStreamP);        
  837.     fAbortPBP->csParam.abort.userDataPtr= nil;
  838.     PBControlSync((ParmBlkPtr) fAbortPBP);
  839.     if (fAbortPBP->ioResult == 0 || fAbortPBP->ioResult == connectionDoesntExist || fAbortPBP->ioResult == connectionTerminated) 
  840.         return;
  841.     return;
  842. #endif
  843. }
  844.  
  845.  
  846.  
  847.  
  848.  
  849. // Send functions ......................................
  850.  
  851. #ifdef UseMacTCPApi
  852. void DTCP::SendData( Ptr wdsPtr, Boolean immediately)
  853. {    
  854.     // note: max wdsPtr[i].length == 65535 (min == 1)
  855.     
  856.     SetupTCPblock( fPBP, TCPSend, fStreamP);        
  857.     fPBP->csParam.send.ulpTimeoutValue    = (unsigned char)Min(180, fTimeout/60);     
  858.     fPBP->csParam.send.ulpTimeoutAction    = 1;     // 0 == report, 1 == abort
  859.     fPBP->csParam.send.validityFlags    = 0xC0;    
  860.     fPBP->csParam.send.pushFlag                = immediately; // send it at once == 1     
  861.     fPBP->csParam.send.urgentFlag            = false;    
  862.     fPBP->csParam.send.wdsPtr                    = (Ptr) wdsPtr;    
  863.     fPBP->csParam.send.userDataPtr        = NULL;
  864.     PBControlAsync((ParmBlkPtr) fPBP);
  865.  
  866.     while (fPBP->ioResult == inProgress) StreamYieldTime();
  867.     FailOSErr(fPBP->ioResult);
  868. }
  869. #endif
  870.  
  871.  
  872.  
  873. void DTCP::SendBytes(void *data, long datasize, Boolean immediately)
  874. {
  875.     char*    pp     =  (char*) data;
  876.     long len = datasize;
  877.     
  878. #ifndef UseMacTCPApi
  879.     len= SockWrite( fSocket, data, datasize);
  880. #else
  881.     while (len > 0) {
  882.         unsigned short partLen = len > 16384 ? 16384 : short(len);
  883.         ((wdsEntryPtr)fWdsEntryP)[0].ptr = pp;
  884.         ((wdsEntryPtr)fWdsEntryP)[0].length = partLen;
  885.         SendData( (Ptr)fWdsEntryP, immediately && (len - partLen <= 0) );
  886.         pp    += partLen;
  887.         len -= partLen;
  888.         StreamYieldTime();
  889.         }
  890.     }
  891. #endif
  892.  
  893.     fLastSentCRLF= ( ((char*)data)[datasize-1] == kLF && ((char*)data)[datasize-2] == kCR);
  894. }
  895.  
  896.  
  897.  
  898. void DTCP::Send( char* data, Boolean immediately)
  899. {
  900.     SendBytes( data, strlen(data), immediately);
  901. }
  902.  
  903. void DTCP::SendCRLF(Boolean EvenIfLastSendHadCRLF, Boolean immediately)
  904. {
  905.     static char    buf[2] = {kCR,kLF};
  906.     if (EvenIfLastSendHadCRLF || !fLastSentCRLF)
  907.         SendBytes( &buf, 2, immediately);
  908. }
  909.  
  910. void DTCP::SendStr( char* s, Boolean addCRLF, Boolean immediately)
  911. {
  912.     SendBytes( s, strlen(s), immediately);
  913.     if (addCRLF)    SendCRLF( true, immediately);
  914. }
  915.  
  916.  
  917.  
  918.  
  919.  
  920.  
  921.  
  922.  
  923. // Receive status functions ......................................
  924.  
  925. long DTCP::CharsAvailable() 
  926. {        
  927. #ifdef UseMacTCPApi
  928.     if (!fStreamIsOpen) 
  929.         return 0;
  930.     else {
  931.         long newbytes= 0;
  932.         if (fHasRDS) newbytes= UnreadCharsWaiting(fWaitingP);
  933.         
  934.         SetupTCPblock( fPBP, TCPStatus, fStreamP);
  935.         fPBP->csParam.status.userDataPtr = NULL;
  936.         PBControlSync((ParmBlkPtr) fPBP);
  937.         Fail(fPBP->ioResult);
  938.         newbytes += fPBP->csParam.status.amtUnreadData;  
  939.         return newbytes;
  940.         }
  941.     }
  942.     
  943. #else
  944.     if (!fStreamIsOpen) 
  945.         return 0;
  946.     else if (!fConnectionIsOpen)  
  947.         return 0;
  948.     else {
  949.         long charsleft =  0;
  950.         
  951.         if (fReadSave) charsleft= fReadSaveLen;
  952.         
  953.             // ?? how do we deal w/ this in sockets ??
  954.             // do we need to use select(n, readfds, writefds, errfds, timeout);
  955.             // doesn't select() set a file descripttor for async operations??
  956. #if 0
  957.         long readarray[1] = { fSocket };
  958.         long maxtime = 1;
  959.         short result= SockSelect( 1, readarray, NULL, NULL, maxtime);
  960.         if (result > 0) charsleft++; // don't know how many...
  961. #endif
  962.  
  963.         return charsleft;
  964.         }
  965. #endif 
  966. }
  967.  
  968. void DTCP::EatResponseLine(void)
  969. {
  970.     (void) MemFree( RecvLine()); //RecvUpTo( true, kLF, NULL));
  971.     (void) MemFree( RecvChars( CharsAvailable() ));    
  972. }
  973.  
  974.  
  975. long DTCP::TotalBytesReceived()
  976. // total over life of the DTCP (since init), or multiple calls to RecvUpTo, RecvChunk ...
  977. {
  978.     return fBytesread; // fResultTotal;
  979. }
  980.  
  981. long DTCP::NewBytesReceived()
  982. // total only for last read 
  983. {
  984.     return fResultNew;
  985. }
  986.  
  987. void DTCP::NullTerm( Boolean turnon)
  988. {
  989.     fNullTerm = turnon;
  990. }
  991.  
  992. long DTCP::ConnectTime()
  993. {
  994.     if (fConnectionIsOpen) fConnectTime = GetSecs() - fStartTime;
  995.     return fConnectTime;
  996. }
  997.  
  998.  
  999.  
  1000. // Receive functions ......................................
  1001.  
  1002. #ifdef UseMacTCPApi
  1003. void DTCP::ReturnRds( Ptr theRdsArray)
  1004. {
  1005.     if (theRdsArray == fRdsEntryP) { 
  1006.         if (((rdsEntryPtr)theRdsArray)[0].length > 0) {
  1007.             SetupTCPblock( fReceivePBP, TCPRcvBfrReturn, fStreamP);                        
  1008.             fReceivePBP->csParam.receive.rdsPtr = theRdsArray;    
  1009.             fReceivePBP->csParam.receive.userDataPtr= NULL;
  1010.             PBControlSync((ParmBlkPtr) fReceivePBP);
  1011.             }
  1012.       for (short ir=0; ir<kNumRds; ir++) {
  1013.             ((rdsEntryPtr)theRdsArray)[ir].length = 0;
  1014.             ((rdsEntryPtr)theRdsArray)[ir].ptr = NULL;
  1015.             }
  1016.         }
  1017.     else if (theRdsArray == fWaitingP) { 
  1018.         Boolean hasdata= true;
  1019.       for (short ir=0; ir<kNumRds; ir++) {
  1020.             if (hasdata) {
  1021.                 long len= ((rdsEntryPtr)theRdsArray)[ir].length;
  1022.                 char *cp= ((rdsEntryPtr)theRdsArray)[ir].ptr;
  1023.                 if (len == 0) hasdata= false;
  1024.                 if (cp) free( cp); //DisposeIfPtr( cp); 
  1025.                 }
  1026.             ((rdsEntryPtr)theRdsArray)[ir].ptr = NULL;
  1027.             ((rdsEntryPtr)theRdsArray)[ir].length = 0;
  1028.             }
  1029.         }
  1030.     fHasRDS = false;
  1031. }
  1032. #endif
  1033.  
  1034. #ifdef UseMacTCPApi
  1035. void DTCP::NoCopyRead( Ptr theRdsArray, short numEntries)
  1036. {
  1037.     SetupTCPblock( fReceivePBP, TCPNoCopyRcv, fStreamP);                        
  1038.     fReceivePBP->csParam.receive.commandTimeoutValue = (unsigned char) fTimeout/60; //1;     // short wait
  1039.     fReceivePBP->csParam.receive.urgentFlag        = 0;    
  1040.     fReceivePBP->csParam.receive.markFlag            = 0;    
  1041.     fReceivePBP->csParam.receive.rdsPtr                = theRdsArray;    
  1042.     fReceivePBP->csParam.receive.rdsLength        = numEntries;    
  1043.     fReceivePBP->csParam.receive.userDataPtr    = NULL;
  1044.     PBControlAsync((ParmBlkPtr) fReceivePBP);
  1045.     fHasRDS = true;
  1046.     while (fReceivePBP->ioResult == inProgress) StreamYieldTime();
  1047.     fHasRDS = (fReceivePBP->ioResult == 0); // ???
  1048. }
  1049. #endif
  1050.  
  1051.  
  1052.  
  1053. #ifdef UseMacTCPApi
  1054. void DTCP::SaveUnreadArray()
  1055. {
  1056.     // copy remainder of fRdsEntryP to fWaitingP
  1057.     short    i, j;
  1058.     long    len;
  1059.     Boolean hasdata = true, haswait= false;
  1060.     rdsEntryPtr waitArray = (rdsEntryPtr)fWaitingP;
  1061.     rdsEntryPtr theRdsArray = (rdsEntryPtr)fRdsEntryP;
  1062.     for (i= fWaitSeg, j=0; hasdata && i<kNumRds; i++, j++) {
  1063.         waitArray[j].length = len = theRdsArray[i].length;
  1064.         //waitArray[j].ptr = theRdsArray[i].ptr;
  1065.         if (len == 0) hasdata = false;
  1066.         if (hasdata) {
  1067.             haswait= true;
  1068.             waitArray[j].ptr = (char*) malloc( len); //NewPtr( len); 
  1069.             memcpy( waitArray[j].ptr, theRdsArray[i].ptr, len);
  1070.             }
  1071.         }
  1072.             // and return to tcp -- !? IS THIS LEGAL !?!?
  1073.     ReturnRds(fRdsEntryP);                        
  1074.     if (haswait) fHasRDS= true;    // we still have fWaitingP
  1075.     fWaitSeg= 0;        // data starts in item 0, at index fWaitIndx
  1076. }
  1077. #endif
  1078.  
  1079. #ifdef UseMacTCPApi
  1080. long DTCP::UnreadCharsWaiting(Ptr aRdsArray)
  1081. {
  1082.     long newbytes= 0;
  1083.     Boolean hasdata= true;
  1084.     rdsEntryPtr theRdsArray = (rdsEntryPtr)aRdsArray;
  1085.     for (short i=fWaitSeg; hasdata && i<kNumRds; i++) {
  1086.         long len= theRdsArray[i].length;
  1087.         if (len == 0) hasdata= false;
  1088.         else if (i == fWaitSeg) len -= fWaitIndx;
  1089.         newbytes += len;
  1090.         }
  1091.     return newbytes;
  1092. }
  1093. #endif
  1094.  
  1095.  
  1096. Boolean gStoppedatLF = false;
  1097.  
  1098. #ifdef UseMacTCPApi
  1099. void DTCP::CopyReadArray(char **datap, long datasize, long &bytesReceived, 
  1100.                                                     Ptr aRdsArray, short numEntries,
  1101.                                                     Boolean convertnewline, Boolean checklf, Boolean stopatlf)
  1102. {
  1103.     rdsEntryPtr theRdsArray= (rdsEntryPtr)aRdsArray;
  1104.     Boolean done = false;
  1105.     if (stopatlf) convertnewline= true;
  1106.     gStoppedatLF = false;    
  1107.     
  1108.     while ( bytesReceived<datasize 
  1109.         && fWaitSeg<numEntries  && !done
  1110.         && theRdsArray[fWaitSeg].length > 0 ) {
  1111.             long newindx= 0;
  1112.             long newbytes = theRdsArray[fWaitSeg].length - fWaitIndx;
  1113.             long maxnewbytes = datasize - bytesReceived;
  1114.             
  1115.             if (convertnewline) {
  1116.                 register char* cp= theRdsArray[fWaitSeg].ptr + fWaitIndx;
  1117.                 register char* bp= *datap;
  1118.                 register char  c;
  1119.                 register long     j;
  1120.                 register long usedbytes= 0;
  1121.                 for (j= 0; j<newbytes && usedbytes<maxnewbytes; j++) {  
  1122.                     if ((c= *cp++) == kLF) {
  1123.                         if (fLastc != kCR) { *bp++ = kCR; usedbytes++; }
  1124.                         else if (checklf && fLast2c == '.' && fLast3c == kLF) {
  1125.                             bp -= 3; // drop dot-cr from bufat
  1126.                             usedbytes -= 3;
  1127.                             fEndofMessage= true;
  1128.                             done= true; 
  1129.                             j = newbytes;
  1130.                             }
  1131.                         if (stopatlf)    { 
  1132.                             gStoppedatLF = true;    
  1133.                             done= true; j= newbytes; 
  1134.                             }
  1135.                         }
  1136.                     else
  1137.                         { *bp++ = c; usedbytes++; }
  1138.                     fLast3c = fLast2c;
  1139.                     fLast2c    = fLastc;
  1140.                     fLastc    = c;
  1141.                     }
  1142.                 if (usedbytes == maxnewbytes && usedbytes < newbytes) 
  1143.                     newindx = fWaitIndx + newbytes;
  1144.                 newbytes = usedbytes;
  1145.                 }
  1146.                 
  1147.             else {
  1148.                 if (newbytes > maxnewbytes) {
  1149.                     newbytes= maxnewbytes;
  1150.                     newindx = fWaitIndx + newbytes;
  1151.                     }
  1152.                 memcpy( *datap, theRdsArray[fWaitSeg].ptr + fWaitIndx, newbytes);
  1153.                 }
  1154.             
  1155.             *datap += newbytes;
  1156.             bytesReceived += newbytes;
  1157.             fWaitIndx= newindx;
  1158.             if (!newindx) fWaitSeg++;
  1159.             }
  1160.  
  1161.     if (theRdsArray[fWaitSeg].length == 0 || (bytesReceived<datasize && !stopatlf))  
  1162.         ReturnRds(aRdsArray);
  1163. }
  1164. #endif
  1165.  
  1166.  
  1167.  
  1168. void DTCP::ReceiveData(void *data, long datasize, long &bytesReceived, Boolean stopatlf)
  1169. {
  1170.     short    err= 0;
  1171.     long startTick= GetSecs();
  1172.     long oldBytes = fBytesread;
  1173.     Boolean done    = datasize < 1;
  1174.     bytesReceived = 0;
  1175.     char*    datap        = (char*) data;
  1176.         
  1177. #ifdef UseMacTCPApi
  1178.     while ( bytesReceived<datasize && !done ) {
  1179.         if (fHasRDS) { 
  1180.             CopyReadArray( &datap, datasize - bytesReceived, bytesReceived, fWaitingP, kNumRds,
  1181.                                                     stopatlf, false, stopatlf);
  1182.             if (bytesReceived >= datasize || gStoppedatLF) done= true; 
  1183.             }
  1184.         if (!done) {
  1185.             fWaitSeg= fWaitIndx= 0;
  1186.             ((rdsEntryPtr)fRdsEntryP)[0].length = 0;
  1187.             NoCopyRead( fRdsEntryP, kNumRds-1);
  1188.             err = fReceivePBP->ioResult;
  1189.             done |= (fReceivePBP->ioResult != commandTimeout); // ???
  1190.             //done |= (GetSecs() - startTick >= fTimeout);
  1191.             
  1192.             CopyReadArray( &datap, datasize - bytesReceived, bytesReceived, fRdsEntryP, kNumRds,
  1193.                                                         stopatlf, false, stopatlf);
  1194.             if (((rdsEntryPtr)fRdsEntryP)[fWaitSeg].length > 0) 
  1195.                 SaveUnreadArray();
  1196.             if (bytesReceived >= datasize || gStoppedatLF) done= true;
  1197.             }
  1198.         }        
  1199.         
  1200. #else
  1201.     long count;
  1202.     
  1203.     if (fReadSave) {
  1204.         // read from local buffer...
  1205.         count= Min( datasize, fReadSaveLen);
  1206.         if (stopatlf) {
  1207.             char* lfp= (char*) MemChr( fReadSave, '\n', count);
  1208.             if (lfp) count= lfp - fReadSave;
  1209.             }
  1210.         MemCpy( datap, fReadSave, count);
  1211.         datap += count;
  1212.         bytesReceived += count;
  1213.         datasize -= count;
  1214.         if (datasize<1) done= true;
  1215.         fReadSaveLen -= count;
  1216.         if (fReadSaveLen > 0) {
  1217.             char* tmp= (char*) MemDup( fReadSave+count, fReadSaveLen);
  1218.             MemFree( fReadSave);
  1219.             fReadSave= tmp;
  1220.             }
  1221.         else {
  1222.             fReadSave= (char*) MemFree( fReadSave);
  1223.             fReadSaveLen= 0;
  1224.             }
  1225.         }
  1226.  
  1227.     while ( !done && (count= SockRead( fSocket, datap, datasize)) > 0) {
  1228.         if (stopatlf) {
  1229.             char *lfp= (char*) MemChr(datap, '\n', count);
  1230.             if (lfp) {
  1231.                 long newcount= lfp - datap;
  1232.                 if (newcount < count) {
  1233.                     fReadSaveLen= count - newcount;
  1234.                     fReadSave=    (char*) MemDup( lfp+1, fReadSaveLen);
  1235.                     count= newcount;
  1236.                     }
  1237.                 done= true;
  1238.                 }
  1239.             }
  1240.         datap     += count;
  1241.         bytesReceived += count;
  1242.         datasize -= count;
  1243.         if (datasize<1) done= true;
  1244.         }
  1245. #endif
  1246.     
  1247.     fBytesread= oldBytes + bytesReceived;
  1248.         
  1249.     StreamYieldTime();
  1250. }
  1251.  
  1252.  
  1253.  
  1254. char* DTCP::ReadWithChecks( 
  1255.                         long expectedbytes/* = kTCPStopAtclose*/, 
  1256.                         Boolean convertnewline/* = false*/, 
  1257.                         long maxbytes/* = 0*/, 
  1258.                         char* oldbuffer/* = NULL*/)
  1259. {
  1260.     long     bufsize= 0, bytesread = 0, bufadditions = 0, newbytes= 0;
  1261.     long     oldbytes= fBytesread;
  1262.     Boolean stopAtdotcrlf = (expectedbytes == kTCPStopAtdotcrlf);
  1263.     Boolean done = false;
  1264.     short    err = 0;
  1265.     long     startTick = GetSecs();
  1266.  
  1267. #if 0    
  1268.             // no generic analog to FreeMem()?? -- coreavail() ??
  1269.     long freebytes = FreeMem();
  1270.     freebytes = freebytes - (freebytes / 4); 
  1271.     if (maxbytes <= 0) maxbytes= freebytes;
  1272.     else maxbytes = Min( maxbytes, freebytes);
  1273. #endif
  1274.     if (maxbytes <= 0) maxbytes= kMaxReadBuffer;
  1275.     fResultNew= 0; //??
  1276.  
  1277.     if (oldbuffer == NULL) { 
  1278.         bufsize= 0; 
  1279.         //FailNIL( oldbuffer= NewHandle(bufsize));
  1280.         oldbuffer= (char*) MemNew(1);
  1281.         oldbuffer[0]= '\0';
  1282.         }
  1283.     else {
  1284.         //bufsize= GetHandleSize( oldbuffer);
  1285.         bufsize= strlen(oldbuffer); // <<!!! Implies null term !!!
  1286.       //if (bufsize && oldbuffer[bufsize-1] == 0) bufsize--;
  1287.         }
  1288.         
  1289. #ifdef UseMacTCPApi
  1290.  
  1291.     while (!done) {
  1292.         char *bufat, *bufat1;
  1293.         Ptr    rds;
  1294.         Boolean    readingNocopy;
  1295.  
  1296.         if (fHasRDS) { 
  1297.             readingNocopy= false;
  1298.             rds = fWaitingP; 
  1299.             err= 0;
  1300.             }
  1301.         else {
  1302.             readingNocopy= true;
  1303.             rds = fRdsEntryP; 
  1304.             fWaitSeg= fWaitIndx= 0;
  1305.             ((rdsEntryPtr)rds)[0].length = 0;
  1306.             NoCopyRead( rds, kNumRds-1);
  1307.             err = fReceivePBP->ioResult;
  1308.             done |= (fReceivePBP->ioResult != commandTimeout);
  1309.             }
  1310.         
  1311.         long charswaiting= UnreadCharsWaiting(rds);
  1312.         long newbufspace = Min( maxbytes - bufadditions, charswaiting) ;
  1313.  
  1314.         if (charswaiting > maxbytes - bufadditions)  {
  1315.             fResultNew= -1; // flag out-of-memory for TextGopher
  1316.             done= true; 
  1317.             }
  1318.         
  1319.         if (expectedbytes > 0 && newbufspace >= expectedbytes - bytesread) {
  1320.             newbufspace = Max( 0, newbufspace - (expectedbytes - bytesread));
  1321.             fEndofMessage= true;
  1322.             done= true;
  1323.             }
  1324.             
  1325.         if (newbufspace > 0) {
  1326.             bufadditions += newbufspace;
  1327.             //HUnlock( oldbuffer); //?? need this for SetHandleSize?
  1328.             //SetHandleSize( oldbuffer, bufsize + newbufspace); // need +1 to stuff \0 at end
  1329.             //FailMemError();
  1330.             oldbuffer= Nlm_MemMore( oldbuffer, bufsize + newbufspace + 1);
  1331.             
  1332.             //SignedByte savedState = LockHandleHigh(oldbuffer);
  1333.             newbytes= 0;
  1334.             bufat1= bufat= oldbuffer + bufsize;
  1335.             CopyReadArray( &bufat1, newbufspace, newbytes, rds, kNumRds, 
  1336.                                                             convertnewline, stopAtdotcrlf, false);
  1337.  
  1338.             //HSetState(oldbuffer, savedState);
  1339.             bytesread += newbytes;
  1340.             bufsize   += newbytes;
  1341.             fBytesread = oldbytes + bytesread;
  1342.             if (fResultNew>=0) fResultNew += bytesread;
  1343.             }
  1344.         else if (((rdsEntryPtr)rds)[fWaitSeg].length == 0)  {
  1345.             ReturnRds(rds); // normally done in call to CopyReadArray()
  1346.             newbytes= 0;
  1347.             }
  1348.             
  1349.         if (readingNocopy) {
  1350.             if (((rdsEntryPtr)rds)[fWaitSeg].length > 0) SaveUnreadArray();
  1351.             }
  1352.         else {
  1353.             if (newbytes >= newbufspace && ((rdsEntryPtr)rds)[fWaitSeg].length > 0) 
  1354.                 done= true;
  1355.             }
  1356.             
  1357.         //if (GetHandleSize( oldbuffer) > bufsize) 
  1358.         //    SetHandleSize( oldbuffer, bufsize); // shrink down
  1359.             
  1360.         if (fDoShowProgress) ShowProgress();  
  1361.         if (UserBreak()) { err= -1; done = true; }
  1362.         }
  1363.  
  1364. #else //UseMacTCPApi
  1365.  
  1366.     long count;
  1367.     char* bufat;
  1368.     do {
  1369.         count= Min(maxbytes-newbytes,50);
  1370.         if (count>0) {
  1371.             oldbuffer= (char*) MemMore( oldbuffer, bufsize + count + 1);
  1372.             bufat= oldbuffer + bufsize;
  1373.             long newcount = 0;
  1374.  
  1375.             ReceiveData( bufat, count, newcount, stopAtdotcrlf || convertnewline);
  1376.             
  1377.             if (newcount>0 && stopAtdotcrlf) {
  1378.                 if (bufat[0] == '.' && (bufat[1] == kCR || bufat[1] == kLF)) {
  1379.                     count= 0;
  1380.                     }
  1381.                 } 
  1382.             
  1383.             if (newcount>0 && convertnewline) {
  1384.                 // then bufat has only one new line, look at its tail
  1385.                 long n= newcount-1;
  1386.                 if (bufat[n] == kLF) n--;
  1387.                 if (bufat[n] == kCR) n--;
  1388.                 if (n + LineEndSize + 1 > count) {
  1389.                     count= n + 1;
  1390.                     oldbuffer= (char*) MemMore( oldbuffer, bufsize + count + 1);
  1391.                     }
  1392.                 for (short i= 0; i<LineEndSize; i++) bufat[++n]= LineEnd[i];
  1393.                 newcount= n;
  1394.                 }
  1395.             count= newcount;
  1396.             
  1397.             //HSetState(oldbuffer, savedState);
  1398.             bufsize += count;
  1399.             newbytes += count;
  1400.             fBytesread += count;
  1401.             StreamYieldTime();
  1402.             }
  1403.         } while (err == 0 && count > 0);
  1404.         
  1405.     //SetHandleSize( oldbuffer, bufsize);
  1406.     oldbuffer= (char*) MemMore( oldbuffer, bufsize + 1);
  1407.     //FailMemError();
  1408.     //if (bufsize >= (long) fStreamBufferP)    fEndofMessage= true;  // stored file size here.
  1409.     //else if (err == eofErr)  fEndofMessage= true;
  1410.  
  1411. #endif //UseMacTCPApi
  1412.  
  1413.     if (true) { //fNullTerm !!!
  1414.         //SetHandleSize( oldbuffer, bufsize+1);
  1415.         //FailMemError();
  1416.         oldbuffer[bufsize] = '\0'; // always now, no such GetPtrSize(p) for generic systems
  1417.         }
  1418.  
  1419.     if (err != 0)  {
  1420.         fEndofMessage= (Status() != kTCPestablished);
  1421.         }
  1422.         
  1423.     return oldbuffer;
  1424. }
  1425.  
  1426.  
  1427.  
  1428.  
  1429. short DTCP::RecvByte()
  1430. {
  1431. // RecvByte -- Return the next byte in the buffer, reading more in if necessary. 
  1432. // all valid data is >= 0; data < 0 is NODATA 
  1433.     unsigned char b;
  1434.     long bytesread= 0;
  1435.  
  1436.     ReceiveData( &b, 1, bytesread);
  1437.         
  1438.     if (bytesread>0) return b; 
  1439.     //else if (gUseTCP) return fReceivePBP->ioResult; // MacTCPCommonTypes error codes
  1440.     else return -1;            
  1441. }
  1442.  
  1443.   
  1444. char* DTCP::RecvChars(long readCount)
  1445.     long  thisread;
  1446.     long    numread= 0;
  1447.     long  bufsize= readCount;
  1448.  
  1449.     if (!fConnectionIsOpen) return NULL;
  1450.     if (readCount < 0) Fail("TCP.RecvChars: invalid count"); 
  1451.  
  1452.     //if (fNullTerm) fResultHand = NewHandle(bufsize+1);
  1453.     //else fResultHand = NewHandle(bufsize+1);
  1454.     //FailMemError();
  1455.     //HLock( fResultHand);
  1456.     fResultHand= (char*) MemNew(bufsize+1);
  1457.     
  1458.     char* p= fResultHand;
  1459.     if (readCount > 0) do {
  1460.         thisread= 0;
  1461.         ReceiveData( p, bufsize, thisread);
  1462.         p += thisread;
  1463.         bufsize -= thisread;
  1464.         numread += thisread;
  1465.     } while (numread < readCount && thisread > 0 && bufsize > 0);
  1466.         
  1467.     if (true) { //fNullTerm)
  1468.         fResultHand[numread] = '\0';
  1469.         }
  1470.     //HUnlock( fResultHand);
  1471.     return fResultHand;
  1472. }
  1473.  
  1474.  
  1475. char* DTCP::RecvChunk( long maxChunk, char* oldChunk)
  1476. {    
  1477.     Boolean done; 
  1478.     long    thisread, count, oldchunksize, newchunksize= 0;
  1479.      // note: maxChunk >= newchunksize, oldchunksize is separate
  1480.     
  1481.     fResultNew= 0;
  1482.     fResultHand= oldChunk;
  1483.     if (!fConnectionIsOpen) return oldChunk;
  1484.     
  1485.     if (maxChunk <= 0) 
  1486.         maxChunk = kMaxReadBuffer; //maxChunk= FreeMem() - (FreeMem() / 5); 
  1487.     
  1488.     if (oldChunk == NULL) { 
  1489.         oldchunksize= 0; 
  1490.         //FailNIL( oldChunk= NewHandle(oldchunksize));
  1491.         oldChunk= (char*) MemNew(oldchunksize+1);
  1492.         oldChunk[oldchunksize]= '\0';        
  1493.         }
  1494.     else 
  1495.         oldchunksize= strlen( oldChunk); // !! this implies NULL termination for all data !
  1496.         //oldchunksize= GetHandleSize( oldChunk);
  1497.         
  1498.     do {
  1499.         //count= CharsAvailable();
  1500.         //if (count+newchunksize > maxChunk) count= maxChunk-newchunksize;
  1501.         count= maxChunk - newchunksize;
  1502.         if (count>0) {
  1503.             //SetHandleSize( oldChunk, oldchunksize+newchunksize+count);  
  1504.             //FailMemError();
  1505.             //SignedByte savedState = LockHandleHigh(oldChunk);
  1506.             
  1507.             oldChunk= (char*) MemMore( oldChunk, oldchunksize + newchunksize + count + 1);
  1508.             char* p= oldChunk + oldchunksize + newchunksize;
  1509.             thisread = 0;
  1510.             ReceiveData( p, count, thisread);
  1511.             //HSetState(oldChunk, savedState);
  1512.             newchunksize += thisread;
  1513.             }
  1514.  
  1515.         fEndofMessage= (Status() != kTCPestablished);
  1516.         if (fDoShowProgress) ShowProgress(); 
  1517.         done= (newchunksize >= maxChunk || thisread < 1);
  1518.         if (UserBreak()) {
  1519.             done= true;
  1520.             Fail("User break");  // ?? fail or proceed w/ short read?
  1521.             }
  1522.     } while (!(done || fEndofMessage)); 
  1523.     
  1524.     if (true) { //fNullTerm) 
  1525.         //SetHandleSize( oldChunk, newchunksize+oldchunksize+1);
  1526.         //FailMemError();
  1527.         oldChunk[oldchunksize + newchunksize] = '\0';
  1528.         }
  1529.     fResultNew= newchunksize;
  1530.     fResultHand= oldChunk;
  1531.     return oldChunk;
  1532. }
  1533.  
  1534.  
  1535.  
  1536. char* DTCP::RecvLine() // read up to kLF 
  1537. {
  1538.     const    short    kBufSize = 511;
  1539.     char    buf[kBufSize+1];
  1540.  
  1541.     if (!fConnectionIsOpen) return false;
  1542.     fResultSize= 0;
  1543.     fMaxResultSize= 0;
  1544.     fResultNew= fResultSize;  //save for close calculation
  1545.     fEndofMessage= false;
  1546.  
  1547.     long     bytesread = 0;
  1548.     ReceiveData( buf, kBufSize, bytesread, true);
  1549.     if (bytesread > 0) {
  1550.         fResultSize += bytesread;
  1551.         fResultNew= fResultSize;
  1552.         buf[bytesread]= 0;
  1553.         return StrDup(buf);
  1554.         }
  1555.     else
  1556.         return NULL;
  1557. }
  1558.  
  1559.  
  1560.  
  1561.  
  1562. #ifdef IsThisObsolete
  1563.  
  1564. void DTCP::OpenRecv( Handle oldData, long limit)
  1565. {
  1566.     fResultHand = oldData;         
  1567.     if (fResultHand != NULL) {
  1568.         if (fNullTerm) {
  1569.             fResultSize= GetHandleSize( fResultHand);
  1570.             char *cp= (char*) memchr( *fResultHand, '\0', fResultSize);
  1571.             if (cp) { 
  1572.                 fResultSize= cp - *fResultHand;
  1573.                 SetHandleSize(fResultHand,fResultSize);
  1574.                 }
  1575.             }
  1576.         else
  1577.             fResultSize= GetHandleSize( fResultHand);
  1578.         }    
  1579.     else {
  1580.         fResultHand = NewHandle(0);
  1581.         fResultSize = 0;
  1582.         }
  1583.     fMaxResultSize= 0;
  1584.     fResultNew= fResultSize;  //save for close calculation
  1585.     fEndofMessage= false;
  1586.     
  1587.      if (limit <= 0) limit= FreeMem() - (FreeMem() / 5);  //!! make sure we dont' use all of mem
  1588.     if (limit > 0) fLimitResultSize= limit;
  1589.     else fLimitResultSize= 0; //? save time below if set to max-positive-long ...
  1590. }
  1591.  
  1592.  
  1593. Boolean DTCP::StoreRecvLimit( char b)
  1594. //! this is true when we have reached fLimitResult... and must STOP READING 
  1595. //! current byte b IS stored (if possible) 
  1596. // Put the byte b after the output handle, increasing the handle's size in the process. 
  1597. {
  1598.     fResultSize++;
  1599.     //fResultTotal++;
  1600.     if (fResultSize >= fMaxResultSize) {
  1601.         if (UserBreak())    {
  1602.             // ?? Fail("TCP user break in data receive");  
  1603.             fLimitResultSize= fResultSize;
  1604.             }
  1605.         if (fDoShowProgress) ShowProgress(); // fResultTotal);
  1606.         fMaxResultSize= fResultSize + kRecvChunkSize;
  1607.         SetHandleSize(fResultHand,fMaxResultSize); 
  1608.         FailMemError();
  1609.         }
  1610.     char* p =  *fResultHand + fResultSize-1;
  1611.     *p = b;
  1612.     return ((fLimitResultSize > 0) && (fResultSize >= fLimitResultSize));
  1613. }
  1614.  
  1615.  
  1616. void DTCP::CloseRecv()
  1617. {
  1618.     long endbyte = fResultSize;  
  1619.     if (fNullTerm) endbyte++;
  1620.     if (endbyte > fMaxResultSize) {
  1621.         fMaxResultSize= endbyte + 1;
  1622.         SetHandleSize(fResultHand,fMaxResultSize); 
  1623.         FailMemError();
  1624.         }
  1625.     if (fNullTerm) {
  1626.         char* p = *fResultHand + endbyte-1;
  1627.         *p = 0; // NO MORE NULL terms
  1628.         }
  1629.     fResultNew = fResultSize - fResultNew;
  1630.     //- fResultTotal= fResultTotal + fResultNew; 
  1631.     fMaxResultSize= endbyte;
  1632.     SetHandleSize( fResultHand, fMaxResultSize);
  1633.     if (fDoShowProgress) ShowProgress(); //fResultTotal);
  1634. }
  1635.  
  1636.  
  1637. /*
  1638. TCPRecvUpTo(connectionID,termination character, waitTime,oldString) 
  1639. -- Return a string from the
  1640. TCP connection; return everything available, up to BUT !INCLUDING the termination 
  1641. character (if any). Pass an empty termination character to receive everything 
  1642. available. WaitTime is the amount of time to wait for the input, in ticks 
  1643. (60ths of a second). oldString is what was read the last call (presumably
  1644. terminated due to a time-out).
  1645. */
  1646.  
  1647. Handle DTCP::RecvUpTo(Boolean lookForTerm, char termChar, Handle AppendToThisData)
  1648. {
  1649.     short        inChar;
  1650.     Boolean    done;
  1651.  
  1652.     if (!fConnectionIsOpen) return NULL;
  1653.     OpenRecv( AppendToThisData, 0);
  1654.     done= false;
  1655.     while (!done) {
  1656.         inChar = RecvByte();
  1657.         if (inChar < 0) {
  1658.             fEndofMessage= true;
  1659.             done= true;
  1660.             }                
  1661.         else {         // note: must keep 0, for binary data 
  1662.             if (StoreRecvLimit(inChar)) {
  1663.                 done= true; inChar= -9;
  1664.                 }
  1665.             if (lookForTerm && inChar == termChar) {
  1666.                 //! fEndofMessage= true; << not necessarily !?
  1667.                 done= true; inChar= -8;
  1668.                 }
  1669.             }
  1670.         }
  1671.     CloseRecv();
  1672.     
  1673.     if (inChar == kErrTimedOut) Fail("TCP Connection timed out");
  1674.     else if (inChar == kErrUserBreak) Fail("User break");
  1675.     return fResultHand;
  1676. }
  1677.  
  1678.  
  1679. /*
  1680. TCPRecvMsg(connectionID,waitTime,OKChar,limit) 
  1681. -- Return a message, where a message is
  1682. defined as either a line starting with the OKChar (if the first line does not start with this,
  1683. then the first line is returned surrounded by "•••"; this is an error indication), which should
  1684. be stripped from the message, followed by lines of text until a period on a line be itself is reached,
  1685. which final line is also stipped; or if OKChar is empty, then no initial line, but just lines of text
  1686. until a period on a line by itself, which is stripped. In addition, the following editing is performed on
  1687. the incoming text: linefeeds are removed; control-Hs and the characters immediately preceeding them
  1688. are removed; ".." at the start of lines is changed is "."; tabs are converted to spaces. If waitTime
  1689. ticks go by without reading a whole message, then return "••• time out •••". If limit characters
  1690. are input without reading a whole message, then "••• message too big •••" is appended
  1691. to the truncated message (but it's all read anyway). This routine should be able to read
  1692. SMTP, NNTP, and POP messages.
  1693. */
  1694.  
  1695. Handle DTCP::RecvMsg( char OKChar, long limit)
  1696. {
  1697.     const    char    chRtn = 13;                // ASCII for carriage return. 
  1698.     const    char    chBackspace = 8;                // ASCII for backspace. 
  1699.     const    char    chTab = 9;                        // ASCII for tab. 
  1700.     const    char    chFormfeed = 12;            // ASCII for form feed. 
  1701.     const    char    kTabStops = 8;            // Number of columns per tab stop.   
  1702.     short        err= 0;
  1703.     short        theChar;        // Input character. 
  1704.     short        tabColumn= 0;            // Current column. 
  1705.     Boolean    storeLimit;
  1706.  
  1707. #define ExitRead(e) { err= e; goto exitread; } 
  1708. #define StoreOrDie(c)  { if (StoreRecvLimit(c)) {err= kErrMemFull; goto exitread;} }
  1709. #define nextByte(c) { c= RecvByte(); if (c<0) {err= c; goto exitread;} }
  1710.  
  1711.     if (!fConnectionIsOpen) return NULL;
  1712.     OpenRecv( NULL, limit);
  1713.     
  1714.             // Get the first character. 
  1715.     if (OKChar != 0) nextByte(theChar);
  1716.             // Check if this is a good message or an error. 
  1717.             // If error, return the line, surounded by bullets. 
  1718.     if (theChar != OKChar && OKChar != 0) {
  1719.         char *stmp;
  1720.         for (stmp="••• "; stmp!=NULL; stmp++) StoreOrDie(*stmp);
  1721.         while (theChar != chRtn) {
  1722.             StoreOrDie(theChar);
  1723.             nextByte(theChar);
  1724.             }
  1725.         for (stmp=" •••"; stmp!=NULL; stmp++) StoreOrDie(*stmp);
  1726.         nextByte(theChar);  
  1727.             // Skip the linefeed. 
  1728.         }
  1729.         
  1730.     else {
  1731.                 // Skip the first line. 
  1732.         if (OKChar != 0) do nextByte(theChar) while (theChar != kLF); 
  1733.             // Repeat for each line. 
  1734.         while (true) {
  1735.             nextByte(theChar);
  1736.             if (theChar == '.') {
  1737.                     // Initial period. Might be end-of-message. Check the second char. 
  1738.                 nextByte(theChar);
  1739.                 if (theChar == chRtn) {
  1740.                         // End-of-message. Skip the linefeed and return. 
  1741.                     nextByte(theChar);
  1742.                     fEndofMessage= true;
  1743.                     break;    // exit the WHILE loop....
  1744.                     }
  1745.                 else      // Otherwise, output the initial period.
  1746.                     storeLimit= StoreRecvLimit('.');
  1747.                                 // Plus the next char if it wasn't a doubled initial period. 
  1748.                 if (theChar != '.') storeLimit |= StoreRecvLimit(theChar);
  1749.                 if (storeLimit) ExitRead(kErrMemFull);
  1750.                 nextByte(theChar);
  1751.                 }
  1752.                 
  1753.                 // Do the rest of the line. 
  1754.             while (theChar != kLF) {
  1755.                 if (theChar == chTab) do { // Space out to the tab stop << NOT FOR GOPHER 
  1756.                         StoreOrDie(' ');
  1757.                         tabColumn++;
  1758.                     } while (! (tabColumn % kTabStops) == 0);
  1759.                 else if (theChar == chBackspace) {
  1760.                     // Back up one, if there's anything to back up over. 
  1761.                     if (tabColumn > 0 && fResultSize > 0) {
  1762.                         fResultSize--;
  1763.                         tabColumn--;
  1764.                         }
  1765.                     }                        
  1766.                 else if (theChar == chFormfeed) {
  1767.                     // Insert a page-break. 
  1768.                     short i;
  1769.                     for (i = 0; i<20; i++) StoreOrDie('-');
  1770.                     for (i = 0; i< 3; i++) StoreOrDie(chRtn);
  1771.                     }                        
  1772.                 else if (theChar != kLF) {
  1773.                     // Just put the character out straight, and adjust the tabbing. 
  1774.                     StoreOrDie(theChar);
  1775.                     if (theChar == chRtn) tabColumn = 0;
  1776.                     else tabColumn++;
  1777.                     }
  1778.                 nextByte(theChar);
  1779.                 }
  1780.             }
  1781.         }
  1782.     err= 0;
  1783.     
  1784. exitread:
  1785.     CloseRecv();  
  1786.     if (err == kErrTimedOut) Fail("TCP Connection timed out");  
  1787.     else if (err == kErrUserBreak) Fail("User break");  
  1788.     return fResultHand;   
  1789.  
  1790. #undef ExitRead
  1791. #undef StoreOrDie
  1792. #undef nextByte
  1793. }
  1794.  
  1795.  
  1796. #endif //IsThisObsolete
  1797.  
  1798.  
  1799.